package cn.zhaopin.starter.security.manager;

import cn.zhaopin.common.constant.LogConstants;
import cn.zhaopin.common.context.UserContextHolder;
import cn.zhaopin.starter.security.basic.CodeException;
import cn.zhaopin.starter.security.basic.UserContext;
import cn.zhaopin.starter.security.cache.SecurityCacheUtil;
import cn.zhaopin.starter.security.common.SecurityConstant;
import cn.zhaopin.starter.security.properties.SecurityProperties;
import cn.zhaopin.starter.security.service.SecurityUserDetailService;
import cn.zhaopin.starter.security.token.PhoneAndSmsAuthenticationToken;
import cn.zhaopin.starter.security.user.SecurityContextHolderBuilder;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

/**
 * Description: 手机号验证码授权登陆
 *
 * @author zuomin (myleszelic@outlook.com)
 * @date: 2021/10/29-13:52
 */
@Slf4j
public class PhoneAndSmsAuthenticationManager implements ReactiveAuthenticationManager {

    public final SecurityUserDetailService securityUserDetailService;

    public final SecurityProperties securityProperties;

    public final SecurityCacheUtil securityCacheUtil;


    public PhoneAndSmsAuthenticationManager(SecurityUserDetailService securityUserDetailService, SecurityProperties securityProperties, SecurityCacheUtil securityCacheUtil) {
        this.securityUserDetailService = securityUserDetailService;
        this.securityProperties = securityProperties;
        this.securityCacheUtil = securityCacheUtil;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        PhoneAndSmsAuthenticationToken authenticationToken = (PhoneAndSmsAuthenticationToken) authentication;

        String smsCodeCacheKey = SecurityConstant.CACHE.LOGIN_SMS_YZM + ":" + authenticationToken.getPrincipal();

        if (!StringUtils.hasLength(securityProperties.getSuperSmsCode()) || !securityProperties.getSuperSmsCode().equals(authenticationToken.getCredentials().toString())) {
            String smsCode = (String) securityCacheUtil.get(smsCodeCacheKey);
            if (!StringUtils.hasLength(smsCode) || smsCode.equals(authentication.getPrincipal().toString())) {
                throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("短信验证码错误,请重新输入").build();
            }
        }
        //销毁短信验证码, 只能用一次
        securityCacheUtil.del(smsCodeCacheKey);

        UserContext user = securityUserDetailService.findUserByPhone(authentication.getPrincipal().toString());
        log.info("UserDetails:{}", user);
        if (user == null) {
            throw CodeException.builder().code(SecurityConstant.NO_PERMISSION_ERROR_CODE).msg("无法获取用户信息").build();
        }

        //将用户信息放入缓存以及日志
        MDC.put(LogConstants.LOG_USER_ID, user.getId().toString());
        UserContextHolder.setUserId(user.getId().toString());

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, authenticationToken.getCredentials(), authenticationToken.getAuthorities());

        //设置securityContextHolder
        SecurityContextHolderBuilder.build(usernamePasswordAuthenticationToken);

        return Mono.just(usernamePasswordAuthenticationToken);
    }
}
